﻿using System;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using LitJson;
using System.Text;
using TwlMakeFace;

/// <summary>
/// 运行时捏脸
/// </summary>
public class MakeFace : MonoBehaviour
{
    [Header("捏脸模型名称")]
    public string modelName;
    [Header("捏脸模型贴图")]
    public Texture2D albedoTex;
    [Header("捏脸眉毛")]
    public Texture2D browTex;

    //模型网格信息
    private ModelMeshInfo meshInfo;
    //骨骼偏移信息
    private ModelBonesOffset boneOffset;
    private Dictionary<int, BonePos> offsetMap = new Dictionary<int, BonePos>();
    //骨骼顶点信息
    private ModelBonesInfo boneInfo;
    private Dictionary<int, BoneInfo> infoMap = new Dictionary<int, BoneInfo>();
    //对称信息
    private ModelBoneOffsetMap offsetLeft2Right;
    private Dictionary<int, int> left2Right = new Dictionary<int, int>();
    //mesh
    private Mesh useMesh;
    //玩家捏脸数据 即为 每个骨骼滑动条信息 map<BoneIndex,value>
    //private string faceData;
    private string faceData = "1:0.5,2:0.5,3:0.6";   
    

    void Start()
    {
        if (string.IsNullOrEmpty(modelName))
        {
            Debug.LogError("请填写正确的模型名称");
            return;
        }
        string meshStr = File.ReadAllText(getBoneMeshPath(modelName));
        meshInfo = JsonMapper.ToObject<ModelMeshInfo>(meshStr);

        string offsetStr = File.ReadAllText(getBoneOffsetPath(modelName));
        boneOffset = JsonMapper.ToObject<ModelBonesOffset>(offsetStr);

        string boneStr = File.ReadAllText(getBoneVertexPath(modelName));
        boneInfo = JsonMapper.ToObject<ModelBonesInfo>(boneStr);

        string ltorStr = File.ReadAllText(getBoneOffsetMapPath(modelName));
        offsetLeft2Right = JsonMapper.ToObject<ModelBoneOffsetMap>(ltorStr);

        initMesh();
        initGUI();
    }

    private GameObject faceObj;
    //原始顶点位置
    private Vector3[] orgVertexs;
    //缓存调整偏移
    private Vector3[] vertexOffset;
    void initMesh()
    {
        useMesh = new Mesh();
        //顶点
        orgVertexs = new Vector3[meshInfo.vertexPos.Count];
        vertexOffset = new Vector3[meshInfo.vertexPos.Count];
        for (int i = 0; i < meshInfo.vertexPos.Count; i++)
        {
            JsonVector3 pos = meshInfo.vertexPos[i];
            orgVertexs[i] = new Vector3((float)pos.x, (float)pos.y, (float)pos.z);
            vertexOffset[i] = Vector3.zero;
        }
        useMesh.vertices = orgVertexs;
        //三角形
        useMesh.triangles = meshInfo.triangles;
        //法线
        Vector3[] normals = new Vector3[meshInfo.normals.Count];
        for (int i = 0; i < meshInfo.normals.Count; i++)
        {
            JsonVector3 pos = meshInfo.normals[i];
            normals[i] = new Vector3((float)pos.x, (float)pos.y, (float)pos.z);
        }
        useMesh.normals = normals;
        //uv
        Vector2[] uv = new Vector2[meshInfo.uv.Count];
        for (int i = 0; i < meshInfo.uv.Count; i++)
        {
            JsonVector2 pos = meshInfo.uv[i];
            uv[i] = new Vector2((float)pos.x, (float)pos.y);
        }
        useMesh.uv = uv;

        faceObj = new GameObject("myface");
        MeshRenderer facemeshRenderer = faceObj.AddComponent<MeshRenderer>();
        facemeshRenderer.material = new Material(Shader.Find("Standard"));
        Texture2D faceTex = CombineTexture.MergeTexture(albedoTex, browTex, 0, 0);
       //Texture faceTex = CombineTexture.BlitMerge(albedoTex, browTex);
        facemeshRenderer.material.SetTexture("_MainTex", faceTex);
        faceObj.AddComponent<MeshFilter>().mesh = useMesh;
        faceObj.transform.rotation = Quaternion.Euler(0, 0, 0);
        faceObj.transform.position = new Vector3(-0.2f, 0, 0);
    }

    private Transform canvansRoot;
    private GameObject tempSlider;
    private Transform gridRoot;
    void initGUI()
    {
        //对称字典
        Dictionary<int, int> right2Left = new Dictionary<int, int>();
        for (int i = 0; i < offsetLeft2Right.mainKey.Count; i++)
        {
            int leftIndex = offsetLeft2Right.mainKey[i];
            int rightIndex = offsetLeft2Right.subKey[i];
            left2Right.Add(leftIndex, rightIndex);
            right2Left.Add(rightIndex, leftIndex);
        }
        canvansRoot = GameObject.Find("Canvas").transform;
        tempSlider = canvansRoot.Find("tempSlider").gameObject;
        gridRoot = canvansRoot.Find("grid");
        //根据骨骼数量 创建
        List<BoneInfo> bonesPool = boneInfo.boneInfo;
        for (int i = 0; i < bonesPool.Count; i++)
        {
            BoneInfo info = bonesPool[i];
            int boneIndex = info.boneIndex;
            bonesValues.Add(info.boneIndex, 0.5f);
            infoMap.Add(info.boneIndex, info);

            if (right2Left.ContainsKey(boneIndex)) continue;
            GameObject slider = GameObject.Instantiate(tempSlider) as GameObject;
            slider.transform.SetParent(gridRoot);
            StringBuilder nameBuilder = new StringBuilder();
            nameBuilder.Append("骨骼_");
            if (left2Right.ContainsKey(boneIndex))
            {
                nameBuilder.Append(boneIndex);
                nameBuilder.Append("_");
                nameBuilder.Append(left2Right[boneIndex]);
            }
            else
            {
                nameBuilder.Append(boneIndex);
            }
            string resultName = nameBuilder.ToString();
            slider.name = resultName;
            UISlider uiSlider = slider.AddComponent<UISlider>();
            uiSlider.setBoneInfo(info, resultName, onBoneValueChange);
        }

        //offsetMap
        for (int i = 0; i < boneOffset.bones.Count; i++)
        {
            BonePos bonePos = boneOffset.bones[i];
            offsetMap.Add(bonePos.boneIndex, bonePos);
        }
    }

    //slider滑动回调
    private Dictionary<int, float> bonesValues = new Dictionary<int, float>();
    //1个顶点可能被多个骨骼影响
    //当有一个骨骼改变的时候 顶点pos = 多个骨骼的结合影响
    //缓存 顶点偏移列表 和 原始顶点位置列表
    //每次有骨骼位置变动 遍历所有骨骼  计算每个骨骼的
    private void onBoneValueChange(int boneIndex, float val)
    {
        if (!bonesValues.ContainsKey(boneIndex))
        {
            Debug.LogError("不存在这个boneIndex " + boneIndex + "   bonesValues.count  " + bonesValues.Count);
            return;
        }
        UnityEngine.Profiling.Profiler.BeginSample("开始调节");
        bonesValues[boneIndex] = val;
        if (left2Right.ContainsKey(boneIndex))
        {
            int rightIndex = left2Right[boneIndex];
            bonesValues[rightIndex] = val;
        }
        //reset
        for (int i = 0; i < vertexOffset.Length; i++)
        {
            vertexOffset[i].x = 0;
            vertexOffset[i].y = 0;
            vertexOffset[i].z = 0;
        }

        foreach (var item in bonesValues)
        {
            int bIndex = item.Key;
            float bVal = item.Value;
            if (bVal == 0.5) continue;

            BoneInfo info = infoMap[bIndex];
            BonePos bonePos = offsetMap[bIndex];
            //计算bone影响的所有顶点
            for (int i = 0; i < info.vertexIndexs.Count; i++)
            {
                int vIndex = info.vertexIndexs[i];
                float vWeight = (float)info.vertexWeights[i];
                BoneVertexOffset boneVertexOffset = null;
                for (int k = 0; k < bonePos.vertexOffsets.Count; k++)
                {
                    if (bonePos.vertexOffsets[k].vertexIndex == vIndex)
                    {
                        boneVertexOffset = bonePos.vertexOffsets[k];
                        break;
                    }
                }

                //最小到原始[0,0.5]
                //原始到最大[0.5,1]
                Vector3 offset = Vector3.zero;
                if (bVal < 0.5)
                {
                    Vector3 min = new Vector3((float)boneVertexOffset.dtMin.x, (float)boneVertexOffset.dtMin.y, (float)boneVertexOffset.dtMin.z);
                    //隐射到0-1
                    float realVal = 1 - bVal * 2;
                    offset = Vector3.Lerp(Vector3.zero, min, realVal);
                }
                else
                {
                    Vector3 max = new Vector3((float)boneVertexOffset.dtMax.x, (float)boneVertexOffset.dtMax.y, (float)boneVertexOffset.dtMax.z);
                    //隐射到0-1
                    float realVal = (bVal - 0.5f) * 2;
                    offset = Vector3.Lerp(Vector3.zero, max, realVal);
                }
                vertexOffset[vIndex] += offset * vWeight;
                //vertexOffset[vIndex] += offset;
            }
        }

        Vector3[] vertexPos = new Vector3[orgVertexs.Length];
        orgVertexs.CopyTo(vertexPos, 0);
        for (int i = 0; i < vertexPos.Length; i++)
        {
            vertexPos[i] += vertexOffset[i];
        }
        useMesh.vertices = vertexPos;
        UnityEngine.Profiling.Profiler.BeginSample("调节完成");
    }


    /// <summary>
    /// 偏移 path
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private string getBoneOffsetPath(string name)
    {
        string subName = name + "_BoneOffset";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        return path;
    }
    /// <summary>
    /// Bone权重顶点 path
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private string getBoneVertexPath(string name)
    {
        string subName = name + "_BoneVertex";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        return path;
    }
    /// <summary>
    /// mesh顶点
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private string getBoneMeshPath(string name)
    {
        string subName = name + "_BoneMesh";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        return path;
    }

    private string getBoneOffsetMapPath(string name)
    {
        string subName = name + "_BoneOffsetMap";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        return path;
    }

}